//
//  PIPlay.m
//  playin
//
//  Created by playin on 2019/5/9.
//  Copyright © 2019年 playin. All rights reserved.
//

#import "PIPlay.h"
#import "PIUtil.h"
#import "net/PIStream.h"
#import "PIVideoDecode.h"
#import "PlayInView.h"
#import "PIDeviceInfo.h"
#import "PIAudioPlay.h"

@interface PIPlay () <PIStreamDelegate, PlayInViewDelegate> {
    dispatch_queue_t _audioQueue;
    dispatch_queue_t _videoQueue;
}

@property (nonatomic, weak) PIVideoDecode *vDecode;
@property (nonatomic, strong) PIStream *sockManager;
@property (nonatomic, strong) PIAudioPlay *audioPlay;
@property (nonatomic, strong) NSDictionary *playDic;
@property (nonatomic, strong) NSData *pixelData;
@property (nonatomic, strong) NSData *spsData;
@property (nonatomic, strong) NSData *ppsData;
@property (nonatomic, assign) CGRect frame;
@property (nonatomic, assign) int orientation;
@property (nonatomic, assign) int duration;
@property (nonatomic, assign) int osType;

@end

@implementation PIPlay

- (instancetype)initWithFrame:(CGRect)frame {
    
    if (self = [super init]) {

        self.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
        self.vDecode = PIVideoDecode.shared;
        self.audioPlay = [[PIAudioPlay alloc] init];
        self.sockManager = [PIStream new];
        self.sockManager.delegate = self;
        self.receiveIDRCount = 0;
        self.sendGestureCount = 0;
        _videoQueue = dispatch_queue_create("tech.playin.video.queue", NULL);
        _audioQueue = dispatch_queue_create("tech.playin.audio.queue", NULL);
    
    }
    return self;
}

- (void)startWithHost:(NSString *)host
                 port:(uint16_t)port
                token:(NSString *)token
             duration:(int)duration
          orientation:(int)orientation
               osType:(int)osType
             gameInfo:(NSDictionary *)gameInfo {
    
    self.orientation = orientation;
    self.duration = duration;
    self.osType = osType;
    self.playDic = gameInfo;
  
    [self.sockManager startStreamWithToken:token
                                deviceName:[PIDeviceInfo deviceName]
                                      host:host
                                      port:port
                                  duration:duration];
    [self configPlayInView];
}

- (void)configPlayInView {
    
    CGFloat deviceWidth = (CGFloat)[[self.playDic objectForKey:@"device_width"] intValue];
    CGFloat deviceHeight = (CGFloat)[[self.playDic objectForKey:@"device_height"] intValue];
    self.playInView = [[PlayInView alloc] initWithFrame:self.frame
                                            deviceWidth:deviceWidth
                                           deviceHeight:deviceHeight
                                            orientation:self.orientation
                                                 osType:self.osType];
    self.playInView.hidden = YES;
    self.playInView.delegate = self;
}

- (void)stopPIPlay {
    
    NSLog(@"[PIPlay] stop");
    [self.sockManager stopStream];
    self.sockManager = nil;
    
    [self stopVideo];
    [self.audioPlay stopAudioPlay];
}

- (void)stopVideo {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.playInView removeFromSuperview];
    });
}

#pragma mark - PISocketManagerDelegate
- (void)onPIStreamEnded:(NSError* )error {
    
    NSLog(@"[PIPlay] onPIStreamEnded%@",error);
    if (error != nil) {
        [self displayPreviousImageBuffer:self.pixelData];
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.delegate && [self.delegate respondsToSelector:@selector(onPIPlaySocketEnd)]) {
            [self.delegate onPIPlaySocketEnd];
        }
    });
}

- (void)onPIStreamReceivedVideoData:(NSData *)data {
   
    dispatch_async(_videoQueue, ^{
        // see H.264-AVC-ISO_IEC_14496-10-2012.pdf
        // Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
        uint8_t *ptr = (uint8_t *)data.bytes;
        uint8_t nalu_type = ptr[4] & 0x1F; // NALU
        //NSLog(@"nalu_type: %u", nalu_type);
        if (nalu_type == 1) { // Coded slice of a non-IDR picture
            [self decodeSlice:data];
        } else if (nalu_type == 5) { // Coded slice of an IDR picture
            [self decodeSlice:data];
            self.pixelData = data;
        } else if (nalu_type == 6) { // Supplemental enhancement information (SEI)
        } else if (nalu_type == 7) { // Sequence parameter set sps pps
            [self synthesizeAndUpdateParameterSet:data];
        } else if (nalu_type == 8) { // Picture parameter set
        }
    });
}

- (void)onPIStreamReceivedAudioData:(NSData *)data {
    
    dispatch_async(_audioQueue, ^{
        [self.audioPlay playWith:data];
    });
}

- (void)onPIStreamRegisterFailure:(NSError *)error {
    
    NSLog(@"[PIPlay] register stream server error: %@", error);
    self.receiveIDRCount = -1;
}

- (void)onPIStreamRegisterSuccess:(NSDictionary *)result {
    
    self.receiveIDRCount = 0;
    int bitChannel = 16;
    int sampleRate = 0;
    int channel = 0;
    if (self.osType == 1) {
        //ios
        sampleRate = 22050;
        channel = 1;
    } else {
        //Android
        sampleRate = 48000;
        channel = 2;
    }
    if ([PIUtil validObj:result]) {
        if ([result.allKeys containsObject:@"stream_info"]) {
            NSDictionary *streamDic = result[@"stream_info"];
            if ([PIUtil validObj:streamDic]) {
                if ([PIUtil validStr:streamDic[@"sps"]]) {
                    self.spsData = [PIUtil dataFromHexString:streamDic[@"sps"]];
                }
                if ([PIUtil validStr:streamDic[@"pps"]]) {
                    self.ppsData = [PIUtil dataFromHexString:streamDic[@"pps"]];
                }
                [self.vDecode updateSps:self.spsData pps:self.ppsData];
            
                if ([PIUtil validObj:streamDic[@"bits_per_sample"]]) {
                    bitChannel = [streamDic[@"bits_per_sample"] intValue];
                }
                if ([PIUtil validObj:streamDic[@"sample_rate"]]) {
                    sampleRate = [streamDic[@"sample_rate"] intValue];
                }
                if ([PIUtil validObj:streamDic[@"channels"]]) {
                    channel = [streamDic[@"channels"] intValue];
                }
            }
        }
    }
    [self.audioPlay configAudioDescriptionWithSampleRate:sampleRate bitsPerChannel:bitChannel channelFrame:channel];
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.delegate && [self.delegate respondsToSelector:@selector(onPIPlayStarted)]) {
            [self.delegate onPIPlayStarted];
        }
    });
}

- (void)synthesizeAndUpdateParameterSet:(NSData *)xps {

    uint8_t *ptr = (uint8_t *)xps.bytes;
    uint8_t nalu_type = ptr[4] & 0x1F;
    if (nalu_type == 7) { // sps pps or 8
        NSData *tmpData = [xps subdataWithRange:NSMakeRange(3, 1)];
        Byte *byte = (Byte *)[tmpData bytes];
        int spsLength = (byte[3] << 24) + (byte[2] << 16) + (byte[1] << 8) + (byte[0]);
        NSData *spsData = [xps subdataWithRange:NSMakeRange(4, spsLength)];
        NSData *ppsData = [xps subdataWithRange:NSMakeRange(spsLength + 8, [xps length] - spsLength - 8)];
        if (![spsData isEqual:self.spsData] || ![ppsData isEqual:self.ppsData]) {
            self.spsData = spsData;
            self.ppsData = ppsData;
            [self.vDecode updateSps:spsData pps:ppsData];
        }
    }
}

- (void)decodeSlice:(NSData *)slice {
    
    self.receiveIDRCount += 1;
    __weak typeof(self) weakSelf = self;
    [self.vDecode decodeData:slice completionHandle:^(CVImageBufferRef imageBuffer) {
        if (imageBuffer == nil) { return; }
        CVPixelBufferRetain(imageBuffer);
        [((PlayInView*)weakSelf.playInView) displayPixelBuffer:imageBuffer];
        CVPixelBufferRelease(imageBuffer);
    }];
}

- (void)displayPreviousImageBuffer:(NSData *)slice {
    
    if (slice == nil) {return;}
    __weak typeof(self) weakSelf = self;
    [self.vDecode decodeData:slice completionHandle:^(CVImageBufferRef imageBuffer) {
        if (imageBuffer == nil) { return; }
        CVPixelBufferRetain(imageBuffer);
        [((PlayInView*)weakSelf.playInView) displayPreviousPixelBuffer:imageBuffer];
        CVPixelBufferRelease(imageBuffer);
    }];
}


#pragma mark - PlayInView delegate

- (void)onPlayInViewTouch:(NSData *)touchData {
    
    self.sendGestureCount += 1;
    [self.sockManager sendTouchData:touchData];
}

- (void)dealloc {
    
    NSLog(@"***** [%@ dealloc] *****", [self class]);
}


@end
